package sv.gob.mh.dgii.jasper;

import ar.com.fdvs.dj.core.DynamicJasperHelper;
import ar.com.fdvs.dj.core.layout.ClassicLayoutManager;
import ar.com.fdvs.dj.domain.DynamicReport;
import ar.com.fdvs.dj.domain.builders.FastReportBuilder;
import java.io.IOException;
import java.sql.Connection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import sv.gob.mh.dgii.jasper.service.ExportingService;

public class Reporte {

    private final static Log log = LogFactory.getLog(Reporte.class);
    public final static String SESSION_REPORTS_MAP = "sessionReportsMap";
    public final static String DYNAMIC_JASPER = "dynamicJasper";
    public final static String CURRENT_REPORT_SESSION_VAR = "CURRENT_REPORT";
    public final static String CURRENT_REPORT_NAME_SESSION_VAR = "CURRENT_REPORT_NAME";
    public final static String REPORT_PARAMETER_PREFIX = "jpr_rpt";
    public final static String REPORT_DATA_PARAMETER_PREFIX = "jpr_sql";
    public final static String REPORT_DATA_XML_PARAMETER_PREFIX = "jpr_xml";
    public final static Integer REPORT_MAX_TRIES = 10;
    public final static int MAX_NUM_ROWS_PER_REPORT = 5000;
    protected Connection conexionJDBC;
    // reportData
    private ReportData reportData;
    protected static Map<String, String> tipoMimeTypeMap = new HashMap<String, String>();
    private JasperPrint jasperPrint;

    public Reporte() {
        reportData = new ReportData();
    }

    public Reporte(String modulo, String nombre) {
        this();
        reportData.setModulo(modulo);
        reportData.setNombre(nombre);
        reportData.setNombreLogico(getRandomName(nombre));
        reportData.initPath();
        reportData.validateName();
    }

    public Reporte(String modulo, String nombre, Connection conexionJDBC) {
        this(modulo, nombre);
        setConexionJDBC(conexionJDBC);
    }

    public Reporte(String modulo, String nombre, Connection conexionJDBC, boolean cerrarConexionJDBC) {
        this(modulo, nombre, conexionJDBC);
        reportData.setCerrarConexionJDBC(cerrarConexionJDBC);
    }

    public Reporte(String modulo, String nombre, HttpServletRequest request, Connection conexionJDBC) {
        this(modulo, nombre, request);
        setConexionJDBC(conexionJDBC);
    }

    public Reporte(String modulo, String nombre, HttpServletRequest request, Connection conexionJDBC, boolean cerrarConexionJDBC) {
        this(modulo, nombre, request, conexionJDBC);
        reportData.setCerrarConexionJDBC(cerrarConexionJDBC);
    }

    public Reporte(String modulo, String nombre, HttpServletRequest request) {
        this(modulo, nombre);
        initPageParameters(request);
    }

    public Reporte(FastReportBuilder drb, List list, Map params, String nombre, String tipoMime, boolean descargando) {
        this();
        reportData.setDescargando(descargando);
        reportData.setTipoMime(tipoMime);
        reportData.setNombre(nombre);
        reportData.setModulo(DYNAMIC_JASPER);
        reportData.setNombreLogico(getRandomName(nombre));
        reportData.setUsingConnection(false);
        reportData.setParametros(params);
        reportData.setFastReportBuilder(drb);
        reportData.setListadoDynamic(list);

    }

    @Override
    public String toString() {
        return reportData.toString();
    }

    public void executeBeforeReport(HttpServletRequest request, HttpServletResponse response) {
        // INFO: implement executeBeforeReport method
    }

    public void executeAfterReport(HttpServletRequest request, HttpServletResponse response) {
        // INFO: implement executeAfterReport method
    }

    public void executeReport(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
        ServletOutputStream out = response.getOutputStream();
        try {
            if (getReportData() != null && getReportData().getModulo() != null && getReportData().getModulo().toLowerCase().trim().contains(DYNAMIC_JASPER)) {
                DynamicReport dr = getReportData().getFastReportBuilder().build();
                JasperReport jr = DynamicJasperHelper.generateJasperReport(dr, new ClassicLayoutManager(), getReportData().getParametros());
                JRDataSource ds = new JRBeanCollectionDataSource(getReportData().getListadoDynamic());
                this.jasperPrint = JasperFillManager.fillReport(jr, getReportData().getParametros(), ds);
            } else if (jasperPrint == null) {
                jasperPrint = JasperFillManager.fillReport(getReportData().getRutaReportes() + getReportData().getNombreArchivo(), getReportData().getParametros());
            }
            out.flush();
            export(request, response, out);
        } catch (JRException ex) {
            log.error(ex);
        } catch (Exception ex) {
            log.error(ex);
            try {
                request.setAttribute("message", ex.getMessage());
                request.getRequestDispatcher("/WEB-INF/view/reportError.jsp").forward(request, response);
                throw new RuntimeException(ex);
            } catch (Exception ex1) {
                log.error(ex1);
            }
        } finally {
            try {
                out.close();
            } catch (Exception e) {
            }
        }
    }

    public final void export(HttpServletRequest request, HttpServletResponse response, ServletOutputStream out) throws JRException, Exception {
        response.setContentType(getReportData().getContetTypeArchivoDescargable());
        response.setHeader("Cache-Control", "no-store");
        response.setHeader("Pragma", "no-store");
        if (reportData.isDescargando() || fileMustBeDownloaded()) {
            response.setHeader("Content-Disposition", "attachment; filename=\"" + getReportData().getNombreDeArchivoDescargable() + "\"");
        }
        if ("xml".equals(getReportData().getTipoMime())) {
            JasperExportManager.exportReportToXmlStream(jasperPrint, out);
        } else if ("csv".equals(getReportData().getTipoMime())) {
            ExportingService exportingService = new ExportingService();
            exportingService.generateCSVOutput(jasperPrint, out);
        } else if ("xls".equals(getReportData().getTipoMime()) || "xlsx".equals(getReportData().getTipoMime())) {
            ExportingService exportingService = new ExportingService();
            exportingService.generateXLSOutput(jasperPrint, out, getReportData().getNombreDeArchivoDescargable());
        } else if ("rtf".equals(getReportData().getTipoMime()) || "doc".equals(getReportData().getTipoMime()) || "docx".equals(getReportData().getTipoMime())) {
            ExportingService exportingService = new ExportingService();
            exportingService.generateRTFOutput(jasperPrint, out, getReportData().getNombreDeArchivoDescargable());
        } else if ("html".equals(getReportData().getTipoMime())) {
            response.setHeader("Content-Type", "text/html; charset=iso-8859-1");
            ExportingService exportingService = new ExportingService();
            exportingService.generateHTMLOutput(request, jasperPrint, out);
        } else {
            ExportingService exportingService = new ExportingService();
            exportingService.generatePDFOutput(jasperPrint, out, getReportData().getNombreDeArchivoDescargable(), getReportData().isDescargando());
        }
    }

    public void closeAll() {
//        try {
//            conexionJDBC.close();
//        } catch (Exception e) {
//        }
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        closeAll();
    }

    /**
     * @return the conexionJDBC
     */
    public Connection getConexionJDBC() {
        return conexionJDBC;
    }

    /**
     * @param conexionJDBC the conexionJDBC to set
     */
    public final void setConexionJDBC(Connection conexionJDBC) {
        this.conexionJDBC = conexionJDBC;
        addParameter("REPORT_CONNECTION", conexionJDBC);
    }

    public final void initPageParameters(HttpServletRequest request) {
        if (request != null) {
            getReportData().addParameters(getPageParametersAsMap(request, REPORT_PARAMETER_PREFIX));
            getReportData().addParameters(getPageParametersAsMap(request, REPORT_DATA_PARAMETER_PREFIX));
            getReportData().addParameters(getParametersAsMapXml(request, REPORT_DATA_XML_PARAMETER_PREFIX));
        }
    }

    private Map<String, Object> getPageParametersAsMap(HttpServletRequest request, String prefix) {
        Map<String, Object> params = new HashMap<String, Object>(0);
        Enumeration enumerator = request.getParameterNames();

        while (enumerator.hasMoreElements()) {
            String element = enumerator.nextElement().toString();
            if (element.startsWith(prefix)) {
                String key = element.replaceAll(REPORT_PARAMETER_PREFIX, "").replaceAll(REPORT_DATA_PARAMETER_PREFIX, "");
                String val = request.getParameter(element).replaceAll(REPORT_PARAMETER_PREFIX, "").replaceAll(REPORT_DATA_PARAMETER_PREFIX, "");
                params.put(key, val);
            }
        }
        return params;
    }

    private Map<String, Object> getParametersAsMapXml(HttpServletRequest request, String prefix) {
        Map<String, Object> params = new HashMap<String, Object>(0);
        Enumeration<?> enumerator = request.getParameterNames();

        while (enumerator.hasMoreElements()) {
            String element = enumerator.nextElement().toString();
            if (element.startsWith(prefix)) {
                String key = element.replaceAll(REPORT_DATA_XML_PARAMETER_PREFIX, "");
                String val = request.getParameter(element).replaceAll(REPORT_DATA_XML_PARAMETER_PREFIX, "");
                params.put(key, val);
            }
        }
        return params;
    }

    public void setReportInSession(HttpSession session) {
        setReportInSession(session, this.getReportData());
    }

    public void setReportInSession(HttpSession session, ReportData reportData) {
        setReportInSession(session, reportData, getReportData().getNombreLogico());
    }

    public static ReportData getReportInSession(HttpSession session) {
        return getReportInSession(session, (String) session.getAttribute(CURRENT_REPORT_NAME_SESSION_VAR), 0);
    }

    public static ReportData getReportInSession(HttpSession session, String nombreReporte) {
        return getReportInSession(session, nombreReporte, 0);
    }

    public static ReportData getReportInSession(HttpSession session, String nombreLogico, Integer intentos) {
        ReportData ret;
        if (nombreLogico == null || "".equals(nombreLogico.trim())) {
            if ((intentos++) >= REPORT_MAX_TRIES) {
                return null;
            }
            ret = getReportInSession(session, (String) session.getAttribute(CURRENT_REPORT_NAME_SESSION_VAR), intentos);
        } else {
            ret = getSessionReport(session, nombreLogico);
        }
        return ret;
    }

    public static void setReportInSession(HttpSession session, ReportData reportData, String nombreReporte) {
        session.setAttribute(CURRENT_REPORT_NAME_SESSION_VAR, nombreReporte);
        addSessionReport(session, reportData);
    }

    public static String getRandomName(String base) {
        return base + String.valueOf((Math.random() * Math.random() * 1024L)).trim().replaceAll("\\.", "9");
    }

    public static String getMimeType(String tipoMime) {
        String ret = "text/plain";
        try {
            if (tipoMimeTypeMap == null) {
                tipoMimeTypeMap = new HashMap<String, String>();
            }
            if (tipoMimeTypeMap.isEmpty()) {
                tipoMimeTypeMap.put("pdf", "application/pdf");
                tipoMimeTypeMap.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
                tipoMimeTypeMap.put("doc", "application/msword");
                tipoMimeTypeMap.put("rtf", "application/rtf");
                tipoMimeTypeMap.put("xls", "application/vnd.ms-excel");
                tipoMimeTypeMap.put("xlsx", "application/vnd.ms-excel");
                tipoMimeTypeMap.put("csv", "text/csv");
                tipoMimeTypeMap.put("bmp", "image/bmp");
                tipoMimeTypeMap.put("bm", "image/bmp");
                tipoMimeTypeMap.put("jpg", "image/jpeg");
                tipoMimeTypeMap.put("jpeg", "image/pjpeg");
                tipoMimeTypeMap.put("tif", "image/tiff");
                tipoMimeTypeMap.put("tiff", "image/tiff");
                tipoMimeTypeMap.put("gif", "image/gif");
                tipoMimeTypeMap.put("txt", "text/plain");
                tipoMimeTypeMap.put("html", "text/html");
                tipoMimeTypeMap.put("htm", "text/html");
                tipoMimeTypeMap.put("shtml", "text/html");
                tipoMimeTypeMap.put("shtm", "text/html");
                tipoMimeTypeMap.put("htmlx", "text/html");
                tipoMimeTypeMap.put("css", "text/css");
                /*
                 Extensión	Mime/Tipo
                 .htm	text/html
                 .html	text/html
                 .shtml	text/html
                 .shtm	text/html
                 .css	text/css
                 .pdf	application/pdf
                 .rtf	application/rtf
                 .ps	application/postscript
                 .eps	application/postscript
                 .hqx	application/mac-binhex40
                 .js	application/javascript
                 .json	application/json
                 .txt	text/plain
                 .text	text/plain
                 .gif	image/gif
                 .jpg	image/jpeg
                 .jpeg	image/jpeg
                 .jpe	image/jpeg
                 .jfif	image/jpeg
                 .pic	image/pict
                 .pict	image/pict
                 .tif	image/tiff
                 .tiff	image/tiff
                 .mpeg	video/mpeg
                 .mpg	video/mpeg
                 .mov	video/quicktime
                 .moov	video/quicktime
                 .aif	audio/aiff
                 .aiff	audio/aiff
                 .wav	audio/wav
                 .ram	audio/x-pn-realaudio
                 .sit	application/x-stuffit
                 .bin	application/x-stuffit
                 .xml	application/xml
                 .z	application/x-zip
                 .zip	application/x-zip
                 .gz	application/x-gzip
                 .tar	application/x-tar
                 */

            }
            ret = tipoMimeTypeMap.get(tipoMime);
            if (ret == null || "".equals(ret.trim())) {
                ret = "text/plain";
            }
        } catch (Exception e) {
        }
        return ret;

    }

    public boolean fileMustBeDownloaded() {
        return reportData.tipoMime.equals("xml")
                || reportData.tipoMime.equals("csv")
                || reportData.tipoMime.contains("xls")
                || reportData.tipoMime.contains("doc")
                || reportData.tipoMime.equals("rtf");
    }

    public static void addSessionReport(HttpSession session, ReportData reportData) {
        Map<String, ReportData> sessionReports = getSessionReportsMap(session);
        sessionReports.put(reportData.getNombreLogico(), reportData);
        setSessionReportsMap(session, sessionReports);
    }

    public static void removeSessionReport(HttpSession session, ReportData reportData) {
        Map<String, ReportData> sessionReports = getSessionReportsMap(session);
        sessionReports.remove(reportData.getNombreLogico());
        setSessionReportsMap(session, sessionReports);
    }

    public static ReportData getSessionReport(HttpSession session, String nombreLogico) {
        ReportData ret = null;
        Map<String, ReportData> sessionReports = getSessionReportsMap(session);
        if (sessionReports != null && sessionReports.size() > 0) {
            ret = sessionReports.get(nombreLogico);
        }
        return ret;
    }

    public static void clearSessionReportsMap(HttpSession session) {
        Map<String, ReportData> sessionReports = getSessionReportsMap(session);
        sessionReports.clear();
        setSessionReportsMap(session, sessionReports);
    }

    public static Map<String, ReportData> getSessionReportsMap(HttpSession session) {
        Map<String, ReportData> sessionReports = (Map<String, ReportData>) session.getAttribute(SESSION_REPORTS_MAP);
        if (sessionReports == null) {
            sessionReports = new HashMap<String, ReportData>();
        }
        return sessionReports;
    }

    public static void setSessionReportsMap(HttpSession session, Map<String, ReportData> reportsMap) {
        session.setAttribute(SESSION_REPORTS_MAP, reportsMap);
    }

    /**
     * @return the reportData
     */
    public ReportData getReportData() {
        if (reportData == null) {
            reportData = new ReportData();
        }
        return reportData;
    }

    /**
     * @param reportData the reportData to set
     */
    public void setReportData(ReportData reportData) {
        this.reportData = reportData;
    }

    public void addParameter(String key, Object value) {
        getReportData().addParameter(key, value);
    }

    public Object getParameter(String key) {
        return getReportData().getParameter(key);
    }

    public String getNombreLogico() {
        return getReportData().getNombreLogico();
    }

    public void setTipoMime(String tipoMime) {
        getReportData().setTipoMime(tipoMime);
    }
}